﻿using System;
using System.Collections.Concurrent;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Iot.TimingDatabase.Models;

namespace Iot.TimingDatabase.Helper;

/// <summary>
///     定义一个内部记录类型，用于存储属性缓存项
/// </summary>
/// <param name="Func">获取属性值的函数</param>
/// <param name="Default">属性的默认值</param>
internal record PropertyCacheItem(Func<object, object> Func, object Default);

/// <summary>
///     定义一个内部记录类型，用于存储扩展值
/// </summary>
/// <param name="Name">属性名称</param>
/// <param name="Value">属性当前值</param>
/// <param name="Default">属性默认值</param>
internal record ExtendedValue(string Name, object? Value, object Default);

internal static class PropertyCache
{
    /// <summary>
    ///     获取属性值的函数 缓存
    /// </summary>
    private static readonly ConcurrentDictionary<Type,
        FrozenDictionary<string, PropertyCacheItem>> Getters
        = new();


    private static readonly Type InterfaceType = typeof(DeviceModule);


    /// <summary>
    ///     获取属性值的函数
    /// </summary>
    /// <param name="type">类型</param>
    /// <param name="propertyName">属性名</param>
    /// <returns></returns>
    private static Func<object, object> GetFunc(Type type, string propertyName)
    {
        // 定义一个参数表达式，表示方法输入的参数，类型为object
        var param = Expression.Parameter(typeof(object));

        // 将输入参数转换为指定的类型
        var castParam = Expression.Convert(param, type);

        // 获取指定类型的指定属性
        var property = Expression.Property(castParam, propertyName);

        // 将属性值转换为object类型
        var convert = Expression.Convert(property, typeof(object));

        // 创建一个Lambda表达式，表示将输入参数转换为指定类型的对象，然后获取该对象的指定属性值，最后将属性值转换为object类型
        return Expression.Lambda<Func<object, object>>(convert, param).Compile();
    }


    /// <summary>
    ///     获取实例的扩展属性值
    /// </summary>
    /// <param name="instance"></param>
    /// <returns></returns>
    public static IReadOnlyList<ExtendedValue> GetExtendedValues(this DeviceModule instance)
    {
        // 获取设备的类型
        var cache = Getters.GetOrAdd(instance.GetType(),
            // 如果缓存中不存在该类型的扩展值，则获取该类型的属性，并筛选出值类型且接口中不存在的属性
            static type =>
            {
                return type.GetProperties()
                    .Where(static a => a.PropertyType.IsValueType && InterfaceType.GetProperty(a.Name) is null)
                    // 将属性转换为冻结字典，键为属性的枚举列名，值为属性缓存项
                    .ToFrozenDictionary(static prop => prop.GetEnumColumnName(prop.Name),
                        prop => new PropertyCacheItem(GetFunc(type, prop.Name), prop.PropertyType.GetDefaultValue()));
            });
        // 返回扩展值列表
        return [.. cache.Select(a => new ExtendedValue(a.Key, a.Value.Func(instance), a.Value.Default))];
    }

    // 根据属性和属性名获取枚举列名
    private static string GetEnumColumnName(this PropertyInfo property, string propertyName)
    {
        // 获取属性上的EnumColumnAttribute特性
        var columnAttr = property.GetCustomAttribute<EnumColumnAttribute>();
        // 如果存在EnumColumnAttribute特性，则返回特性的Name属性值，否则返回属性名
        return columnAttr?.Name ?? propertyName;
    }

    /// <summary>
    ///     获取类型的基础类型
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static Type GetUnderlyingType(this Type type)
    {
        ArgumentNullException.ThrowIfNull(type);
        return Nullable.GetUnderlyingType(type) ?? type;
    }

    /// <summary>
    ///     获取类型的默认值
    /// </summary>
    /// <param name="type"></param>
    /// <returns></returns>
    private static object GetDefaultValue(this Type type)
    {
        return Activator.CreateInstance(GetUnderlyingType(type))!;
    }
}